﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading;
using HashFoo.MigratorFoo;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace HashFoo.MigratorFooTests
{
    /// <summary>
    /// Tests the migration manager worker class.
    /// </summary>
    /// <remarks>
    /// Any migration manager created needs to be explicitly disposed of before a call to NukeDatabase.  If
    /// it is not disposed, then an NHibernate session manager remains active, and holds onto a db connection,
    /// which then does not allow the database to be dropped.
    /// 
    /// It would probably be cleaner to have a class level manager, that gets disposed after each test, but I'm a little
    /// lazy right now.  DMF 20100705
    /// </remarks>
    [TestClass]
    public class MigrationManagerWorkerTests
    {
        [TestInitialize]
        public void Setup()
        {
            log4net.Config.BasicConfigurator.Configure(
                new LogListenerAppender(e => Console.WriteLine(e.RenderedMessage))
                {
                    LogNameFilter = new List<string> { "MigratorFoo" },
                });
        }

        [TestMethod]
        [Description("Tests that the nuke database helper works")]
        public void CanNukeDatabase()
        {
            Helpers.NukeDatabase();
        }

        [TestMethod]
        public void CanWriteSchemaFile()
        {
            Helpers.NukeDatabase();

            var manager = Helpers.CreateDefaultMigrationManagerWorker();

            ClearMigrationsDirectories(manager);
            manager.Generate();

            Assert.IsTrue(Directory.Exists(Helpers.MigrationConfiguration.UpScriptsDirectory));
            Assert.IsTrue(Directory.GetFiles(Helpers.MigrationConfiguration.UpScriptsDirectory).Length > 0);

            manager.Dispose();
        }

        [TestMethod]
        public void CanWriteHelloWorldDownScript()
        {
            Helpers.NukeDatabase();

            var manager = Helpers.CreateDefaultMigrationManagerWorker();

            ClearMigrationsDirectories(manager);
            manager.Generate();

            Assert.IsTrue(Directory.Exists(Helpers.MigrationConfiguration.DownScriptsDirectory));
            Assert.IsTrue(Directory.GetFiles(Helpers.MigrationConfiguration.DownScriptsDirectory).Length > 0);

            manager.Dispose();
        }

        [TestMethod]
        public void CanGetLastVersion()
        {
            var manager = Helpers.CreateDefaultMigrationManagerWorker();
            var version = manager.LastAppliedVersion;

            Console.WriteLine(@"Last applied version: {0}", version);

            manager.Dispose();
        }

        [TestMethod]
        public void CanApply()
        {
            Helpers.NukeDatabase();
            var manager = Helpers.CreateDefaultMigrationManagerWorker();

            ClearMigrationsDirectories(manager);
            manager.Generate();
            manager.Apply();

            manager.Dispose();
        }

        [TestMethod]
        public void CanRollback()
        {
            Helpers.NukeDatabase();
            var manager = Helpers.CreateDefaultMigrationManagerWorker();

            ClearMigrationsDirectories(manager);
            manager.Generate();
            manager.Apply();
            manager.Rollback();

            manager.Dispose();
        }

        [TestMethod]
        [Description("Can detect apply orphan script")]
        public void CanDetectApplyOrphanScript()
        {
            Helpers.NukeDatabase();
            var manager = Helpers.CreateDefaultMigrationManagerWorker();
            ClearMigrationsDirectories(manager);
            Directory.CreateDirectory(manager.Configuration.UpScriptsDirectory);

            const string versionFormat = "yyyyMMdd_HHmmss";
            File.WriteAllText(
                Path.Combine(manager.Configuration.UpScriptsDirectory, DateTime.UtcNow.AddHours(1).ToString(versionFormat) + ".sql"),
                @"create table [OrphanScriptTest](
                        version varchar(2000) not null,
                        createdat datetime not null,
                        primary key (version)
                      );");

            manager.Apply();

            manager.Generate();
            manager.Apply();

            manager.Dispose();
        }

        [TestMethod]
        public void CanReGenerate()
        {
            // this is basically the same as "Reset" since we only have one migration :)
            Helpers.NukeDatabase();
            var manager = Helpers.CreateDefaultMigrationManagerWorker();

            ClearMigrationsDirectories(manager);

            manager.Generate();
            var orignalFiles = Directory.GetFiles(Helpers.MigrationConfiguration.UpScriptsDirectory);
            Assert.IsTrue(orignalFiles.Length == 1);
            var originalMigrationName = Path.GetFileName(orignalFiles[0]);

            manager.Generate(true);
            var regenFiles = Directory.GetFiles(Helpers.MigrationConfiguration.UpScriptsDirectory);
            Assert.IsTrue(regenFiles.Length == 1);
            Assert.AreNotEqual(originalMigrationName, Path.GetFileName(regenFiles[0]));

            manager.Dispose();
        }

        [TestMethod]
        [Description("Tests that the migration manager can get all known db config names")]
        public void CanGetAllDbConfigNames()
        {
            Helpers.NukeDatabase();
            var manager = Helpers.CreateDefaultMigrationManagerWorker();
            var configNames = manager.DbConfigNames;

            Assert.IsTrue(configNames.Length > 0);

            configNames.ToList().ForEach(Console.WriteLine);

            manager.Dispose();
        }

        [TestMethod]
        [Description("Tests that after setting the configuration, we can still run migrations")]
        public void CanSetDbConfigAndApplyMigrations()
        {
            Helpers.NukeDatabase();
            var manager = Helpers.CreateDefaultMigrationManagerWorker();
            var configName = manager.DbConfigNames[1];

            Assert.IsFalse(configName.ToLowerInvariant().Contains("default"));
            manager.SetActiveDbConfig(configName);

            ClearMigrationsDirectories(manager);
            manager.Generate();
            manager.Apply();

            manager.Dispose();   
        }

        private static void ClearMigrationsDirectories(MigrationManagerWorker manager)
        {
            if (Directory.Exists(Helpers.MigrationConfiguration.DownScriptsDirectory))
            {
                Directory.Delete(Helpers.MigrationConfiguration.DownScriptsDirectory, true);
            }

            if (Directory.Exists(Helpers.MigrationConfiguration.UpScriptsDirectory))
            {
                Directory.Delete(Helpers.MigrationConfiguration.UpScriptsDirectory, true);
            }

            // make sure the deletes go through...
            Thread.Sleep(200);
        }
    }
}
